﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace SharpGen.TemplateInterface
{
    public enum AccessModifiers
    {
        amNA = 0,
        amPublic = 1,
        amPrivate = 2,
        amInternal = 3,
        amVirtual = 4,
        amProtected = 5,
        amAbstract = 6,
        amOverride = 7
    }

    public class SharpCodeProvider : IDisposable
    {
        public delegate void WriterDelegate(SharpCodeProvider provider, object data);

        private CodeWriter _writer = null;
        public CodeWriter Writer
        {
            get { return _writer; }
        }

        private string _fileName;
        public string FileName
        {
            get { return _fileName; }
        }

        public SharpCodeProvider(string fileName)
        {
            string path = Path.GetDirectoryName(fileName);
            Directory.CreateDirectory(path);
            _fileName = fileName;
            _writer = new CodeWriter(fileName);
        }

        public void WriteUsing(string value)
        {
            _writer.WriteLine(string.Format("using {0};", value));
        }

        public void WriteNamespace(string value, WriterDelegate[] writerDelegates)
        {
            _writer.WriteLine();
            _writer.WriteLine(string.Format("namespace {0}", value));
            _writer.WriteBegin();
            foreach (WriterDelegate del in writerDelegates)
            {
                del.Invoke(this, new object());
            }

            _writer.WriteEnd();
        }

        public void WriteAttribute(string name, string[] parameters)
        {
            _writer.Write(string.Format("[{0}(", name));
            string paramContent = string.Empty;
            foreach (string parm in parameters)
            {
                paramContent += string.Format("{0}, ", parm);
            }
            if (!string.IsNullOrEmpty(paramContent))
            {
                paramContent = paramContent.Substring(0, paramContent.Length - 2);
            }
            _writer.WriteLine(string.Format("{0})]", paramContent));
        }

        public void WriteClass(AccessModifiers[] accessModifiers, bool isPartial, string name, string parentType, WriterDelegate[] writerDelegates)
        {
            _writer.WriteLine(string.Format("{0}{1} class {2}{3}", GetAccessModifierString(accessModifiers), isPartial ? " partial" : string.Empty, name, string.IsNullOrEmpty(parentType) ? string.Empty : " : " + parentType));
            _writer.WriteBegin();
            if (writerDelegates != null)
            {
                foreach (WriterDelegate del in writerDelegates)
                {
                    del.Invoke(this, name);
                }
            }
            _writer.WriteEnd();
        }

        public void WriteConstructor(AccessModifiers[] accessModifiers, string className, string[] parameterTypes, string[] parameterNames, WriterDelegate content)
        {
            WriteConstructor(accessModifiers, className, parameterTypes, parameterNames, content, null);
        }

        public void WriteConstructor(AccessModifiers[] accessModifiers, string className, string[] parameterTypes, string[] parameterNames, WriterDelegate content, string[] baseParameters)
        {
            _writer.Write(string.Format("{0} {1}(", GetAccessModifierString(accessModifiers), className));

            string parms = string.Empty;
            if (parameterTypes != null && parameterNames != null)
            {
                for (int i = 0; i < parameterTypes.Length; i++)
                {
                    if (parameterNames.Length > i)
                    {
                        parms += string.Format("{0} {1}, ", parameterTypes[i], parameterNames[i]);
                    }
                }
                if (parms.Length > 0)
                {
                    parms = parms.Substring(0, parms.Length - 2);
                }
            }
            _writer.WriteLine(string.Format("{0})", parms));

            if (baseParameters != null && baseParameters.Length > 0)
            {
                parms = string.Empty;
                foreach (string parm in baseParameters)
                {
                    parms += parm + ", ";
                }
                if (parms.Length > 0)
                {
                    parms = parms.Substring(0, parms.Length - 2);
                }
                _writer.WriteLine(string.Format("\t: base({0})", parms));
            }

            _writer.WriteBegin();
            if (content != null)
            {
                content.Invoke(this, null);
            }

            _writer.WriteEnd();
        }

        public void WriteMethod(AccessModifiers[] accessModifiers, string returnType, string name, string[] parameterTypes, string[] parameterNames, WriterDelegate content)
        {
            _writer.Write(string.Format("{0} {1} {2}(", GetAccessModifierString(accessModifiers), string.IsNullOrEmpty(returnType) ? "void" : returnType, name));

            string parms = string.Empty;
            if (parameterTypes != null && parameterNames != null)
            {
                for (int i = 0; i < parameterTypes.Length; i++)
                {
                    if (parameterNames.Length > i)
                    {
                        parms += string.Format("{0} {1}, ", parameterTypes[i], parameterNames[i]);
                    }
                }
                if (parms.Length > 0)
                {
                    parms = parms.Substring(0, parms.Length - 2);
                }
            }
            _writer.WriteLine(string.Format("{0})", parms));

            _writer.WriteBegin();
            if (content != null)
            {
                content.Invoke(this, name);
            }

            _writer.WriteEnd();
        }

        public void WriteField(AccessModifiers[] accessModifiers, string type, string name, string defaultValue)
        {
            _writer.Write(string.Format("{0} {1} {2}", GetAccessModifierString(accessModifiers), type, name));
            if (!string.IsNullOrEmpty(defaultValue))
            {
                _writer.WriteLine(string.Format(" = {0};", defaultValue));
            }
            else
            {
                _writer.WriteLine(";");
            }
        }

        public void WriteProperty(AccessModifiers[] accessModifiers, string type, string name, bool hasSetter, string privateFieldName, bool hasPrivateField)
        {
            WriteProperty(accessModifiers, type, name, null, null, hasSetter, privateFieldName, hasPrivateField);
        }

        public void WriteProperty(AccessModifiers[] accessModifiers, string type, string name, WriterDelegate getDelegate, WriterDelegate setDelegate, bool hasSetter, string privateFieldName, bool hasPrivateField)
        {
            string prvName = string.IsNullOrEmpty(privateFieldName) ? GetPrivatePropertyName(name) : privateFieldName;
            if (hasPrivateField)
            {
                _writer.WriteLine(string.Format("private {0} {1};", type, prvName));
            }
            _writer.WriteLine(string.Format("{0} {1} {2}", GetAccessModifierString(accessModifiers), type, name));
            _writer.WriteBegin();
            if (getDelegate != null)
            {
                _writer.WriteLine("get");
                _writer.WriteBegin();
                getDelegate.Invoke(this, prvName);
                _writer.WriteEnd();
            }
            else
            {
                _writer.WriteLine(string.Format("get {{ return {0}; }}", prvName));
            }
            if (hasSetter)
            {
                if (setDelegate != null)
                {
                    _writer.WriteLine("set");
                    _writer.WriteBegin();
                    setDelegate.Invoke(this, prvName);
                    _writer.WriteEnd();
                }
                else
                {
                    _writer.WriteLine(string.Format("set {{ {0} = value; }}", prvName));
                }
            }
            _writer.WriteEnd();
        }

        public void WriteAddEvent(string eventName, string eventDelegateType, string eventMethod)
        {
            _writer.WriteLine(string.Format("{0} += new {1}({2});", eventName, string.IsNullOrEmpty(eventDelegateType) ? "EventHandler" : eventDelegateType, eventMethod));
        }

        public void WriteRemoveEvent(string eventName, string eventDelegateType, string eventMethod)
        {
            _writer.WriteLine(string.Format("{0} -= new {1}({2});", eventName, string.IsNullOrEmpty(eventDelegateType) ? "EventHandler" : eventDelegateType, eventMethod));
        }

        public void WriteMethodCall(string name, string[] parameters)
        {
            _writer.Write(string.Format("{0}(", name));

            string parms = string.Empty;
            if (parameters != null)
            {
                foreach (string parm in parameters)
                {
                    parms += string.Format("{0}, ", parm);
                }
                if (parms.Length > 0)
                {
                    parms = parms.Substring(0, parms.Length - 2);
                }
            }
            _writer.WriteLine(string.Format("{0});", parms));
        }

        public void WriteAssignment(string fieldName, object value)
        {
            _writer.WriteLine("{0} = {1};", fieldName, value.ToString());
        }

        public void WriteCodeLine(string code)
        {
            if (string.IsNullOrEmpty(code))
            {
                _writer.WriteLine();
            }
            else
            {
                _writer.WriteLine(code);
            }
        }

        public void WriteBegin()
        {
            _writer.WriteBegin();
        }

        public void WriteEnd()
        {
            _writer.WriteEnd();
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (_writer != null)
            {
                _writer.Flush();
                _writer.Close();
                _writer = null;
            }
        }

        #endregion

        public string GetPrivatePropertyName(string publicName)
        {
            //return "_" + publicName[0].ToString().ToLower() + publicName.Substring(1);
            return "_" + publicName;
        }

        private string GetAccessModifierString(AccessModifiers[] accessModifiers)
        {
            string amStr = string.Empty;
            foreach (AccessModifiers am in accessModifiers)
            {
                switch (am)
                {
                    case AccessModifiers.amPublic:
                        amStr += "public ";
                        break;
                    case AccessModifiers.amPrivate:
                        amStr += "private ";
                        break;
                    case AccessModifiers.amInternal:
                        amStr += "internal ";
                        break;
                    case AccessModifiers.amVirtual:
                        amStr += "virtual ";
                        break;
                    case AccessModifiers.amProtected:
                        amStr += "protected ";
                        break;
                    case AccessModifiers.amAbstract:
                        amStr += "abstract ";
                        break;
                    case AccessModifiers.amOverride:
                        amStr += "override ";
                        break;
                    default:
                        break;
                }
            }
            return amStr.TrimEnd();
        }
    }
}
